package com.lyx.rpc.codec;

import com.lyx.rpc.common.utils.SerializationUtils;
import com.lyx.rpc.constants.RpcConstants;
import com.lyx.rpc.protocol.RpcProtocol;
import com.lyx.rpc.protocol.enumeration.RpcType;
import com.lyx.rpc.protocol.header.RpcHeader;
import com.lyx.rpc.protocol.request.RpcRequest;
import com.lyx.rpc.protocol.response.RpcResponse;
import com.lyx.rpc.serialization.api.Serialization;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.CharsetUtil;

import java.io.ByteArrayInputStream;
import java.util.List;

/**
 * @Author by lyx
 *
 * RpcDecoder类比RpcEncoder类复杂些，
 * 因为在RpcDecoder类中，需要考虑当前的数据是请求数据、响应数据还是心跳数据。
 * 同时，在RpcDecoder类中，预留了序列化与反序列化的扩展点，并预留了心跳数据的处理方式。
 */
public class RpcDecoder extends ByteToMessageDecoder implements RpcCodec {
    @Override
    public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 首先检查输入的字节缓冲区中可读字节数是否小于固定的头部长度（RpcConstants.HEADER_TOTAL_LEN == 32）。如果是，说明数据不完整，无法进行解码，直接返回，等待更多数据到来。
        if (in.readableBytes() < RpcConstants.HEADER_TOTAL_LEN) {
            return;
        }
        // 如果可读字节数足够，标记当前的读取索引，以便在后续操作失败时可以重置读取位置。
        in.markReaderIndex();
        // 读取魔数， 消息类型， 状态， 请求ID
        short magic = in.readShort();
        if (magic != RpcConstants.MAGIC) {
            throw new IllegalArgumentException("magic number is illegal, " + magic);
        }

        byte msgType = in.readByte();
        byte status = in.readByte();
        long requestId = in.readLong();

        // 读取序列化类型的字节缓冲区（固定长度为SerializationUtils.MAX_SERIALIZATION_TYPE_COUNT  == 16），并将其转换为字符串表示的序列化类型。（subString去0）
        ByteBuf serializationTypeByteBuf =
            in.readBytes(SerializationUtils.MAX_SERIALIZATION_TYPE_COUNT);
        String serializationType = SerializationUtils.subString(serializationTypeByteBuf.toString(CharsetUtil.UTF_8));

        // 读取数据长度（dataLength），然后再次检查可读字节数是否小于数据长度。如果是，说明数据不完整，重置读取索引，等待更多数据到来，然后返回。
        int dataLength = in.readInt();
        if (in.readableBytes() < dataLength) {
            in.resetReaderIndex();
            return;
        }
        // 如果数据长度足够，创建一个新的字节数组来存储数据，并从字节缓冲区中读取数据。
        byte[] data = new byte[dataLength];
        in.readBytes(data);

        // 根据消息类型字节确定消息类型枚举（RpcType）。如果无法确定消息类型，直接返回。
        RpcType msgTypeEnum = RpcType.findByType(msgType);
        if (msgTypeEnum == null) {
            return;
        }
        // 将之前读取的头部信息设置到这个对象中。
        RpcHeader header = new RpcHeader();
        header.setMagic(magic);
        header.setStatus(status);
        header.setRequestId(requestId);
        header.setMsgType(msgType);
        header.setSerializationType(serializationType);
        header.setMsgLen(dataLength);
        Serialization serialization = getSerialization(serializationType);
        switch (msgTypeEnum) {
            case REQUEST:
                RpcRequest request = serialization.deserialize(data, RpcRequest.class);
                if (request != null) {
                    RpcProtocol<RpcRequest> protocol = new RpcProtocol<>();
                    protocol.setHeader(header);
                    protocol.setBody(request);
                    out.add(protocol);
                }
                break;
            case RESPONSE:
                RpcResponse response = serialization.deserialize(data, RpcResponse.class);
                if (response != null) {RpcProtocol<RpcResponse> protocol = new RpcProtocol<>();
                    protocol.setHeader(header);
                    protocol.setBody(response);
                    out.add(protocol);
                }
                break;
            case HEARTBEAT:
                // TODO
                break;
        }
    }
}
